/**
 * ex5. defer、panic、recover关键字
 *
 * @author cyy
 * @date 2020/10/29
 */
package main

import (
	"fmt"
	"os"
)

func main() {

	/*
		defer关键字
	*/
	fmt.Println("============================ panic关键字 ============================")
	deferTest()

	// defer在命名返回值和匿名返回函数中表现不一样
	fmt.Println("defer在命名返回值和匿名返回函数中表现不一样")

	fmt.Println("------ f1 ------")
	// - 匿名返回函数
	// 		执行顺序①②③，控制台依次输出：1 0
	fmt.Println(f1()) // ③ 打印输出返回的0

	fmt.Println("------ f2 ------")
	// 	- 命名返回值函数
	fmt.Println(f2())

	// 注意：defer注册[要延迟执行的函数]时，该函数所有的参数都需要确定其值
	fmt.Println("\ndefer注册[要延迟执行的函数]时，该函数所有的参数都需要确定其值")
	/*
		顺序：注册AA -> 执行A -> 注册BB -> 执行B -> 执行BB -> 执行AA
			x := 1
			y := 2
			注册AA	// 注册时需要确定 x，deferCalc("A", x, y)) 的值
				x = 1
				执行A：deferCalc("A", x, y) => A 1 2 3
				deferCalc("A", x, y) = 3
			x = 10
			注册BB	// 注册时需要确定 x，deferCalc("B", x, y)) 的值
				x = 10
				执行B：deferCalc("B", x, y) => B 10 2 12
				deferCalc("B", x, y) = 12
			y = 20
			执行BB：deferCalc("BB", x, deferCalc("B", x, y)) => BB 10 12 22
			执行AA：deferCalc("AA", x, deferCalc("A", x, y)) => AA 1 3 4
	*/
	x := 1
	y := 2
	defer deferCalc("AA", x, deferCalc("A", x, y))
	x = 10
	defer deferCalc("BB", x, deferCalc("B", x, y))
	y = 20

	/*
		panic-recover
			go中目前没有异常处理机制，但是可以通过panic-recover模式来处理错误
			panic用于抛出异常，recover用于接收异常
			panic可以在任何地方引发，但recover只有在defer调用的函数中有效
	*/
	fmt.Println("============================ panic关键字 ============================")
	//panicTest()
	// error: runtime error: integer divide by zero
	recoverTest(1, 0)

	write2Txt("/123")

}

func recoverTest(a, b int) {
	defer func() {
		// recover接收异常，只能在defer调用函数中有效
		err := recover()
		if err != nil {
			fmt.Println("error:", err)
		}
	}()
	fmt.Println(a / b)
}

func panicTest() {
	/*
		panic: *** panic ***

		goroutine 1 [running]:
		main.panicTest(...)
			golang-learning/src/main/main.go:79
		main.main()
			golang-learning/src/main/main.go:74 +0x585
	*/
	fmt.Println("before panic")
	panic("*** panic ***")
	fmt.Println("after panic")
}

// 1开始  2结束 1开始 2结束 654321
func deferTest() {
	fmt.Print("1开始 ")
	defer fmt.Println(1)
	defer fmt.Print(2)
	defer fmt.Print(3)
	fmt.Print(" 2结束 ")
	// 也可以将延迟代码放到匿名函数中
	fmt.Print("1开始 ")
	defer func() {
		defer fmt.Print(4)
		defer fmt.Print(5)
		defer fmt.Print(6)
	}()
	fmt.Print("2结束 ")
}

// 匿名返回值
func f1() int {
	var a int
	defer func() { // ② a自增为1，并输出
		a++
		fmt.Println("defer ", a)
	}()
	return a // ① 返回0
}

// 命名返回值
func f2() (a int) {
	defer func() { // ②
		a++
		fmt.Println("defer ", a)
	}()
	return a // ①
}

func deferCalc(index string, a, b int) int {
	res := a + b
	fmt.Println(index, a, b, res)
	return res
}

func write2Txt(path string) {
	file, err := os.OpenFile(path, os.O_WRONLY, 666)
	defer func() { // finally
		file.Close()
		myErr := recover() // catch
		if myErr != nil {
			fmt.Println("error: ", myErr)
		}
	}()

	if err != nil {
		panic("打开文件失败")
	}
	_, err = file.WriteString("直接写入字符串\r\n")
	if err != nil {
		panic("写入文件失败")
	}
}
